#include "world.h"
#include "enemies/testactor.h"
#include "enemies/sprinter.h"
#include "enemies/bigboss.h"
#include "enemies/flyer.h"
#include "items/healthPickup.h"
#include "wiimote_api.h"
#include "wiimote_link.h"
#include "wiimote.h"
#include "motiontracker.h"
#include <OgreTimer.h>
#include "OgreParticleSystem.h"
#include <OgreParticleEmitter.h>
#include "wii.h"
#include <sstream>
#include <string>
#include <iostream>
#include <math.h>

World::World(Ogre::SceneManager *sceneManager)
{
    m_scene = sceneManager;
    m_width = 3000;
    m_height = 1700;
    m_controller = 1;
    m_gestureRunning = false;
    m_gestCount = 0;

    m_logger = Singleton::GetInstance();

    m_player = new Player(this);
    addActor(m_player,Ogre::Vector3(0,0,0));

    m_tracker = new MotionTracker(this, m_player);

    Wii::wiiInit();

    pthread_t wiimoteThread;
    if(pthread_create(&wiimoteThread,NULL,Wii::wiiRun,NULL)){
        std::cout << "ERROR: can't create wiimote thread! \n";
        exit(-1);
    }

    m_numberNodes[0] = m_scene->getRootSceneNode()->createChildSceneNode();
     m_numberNodes[1] = m_scene->getRootSceneNode()->createChildSceneNode();
       m_numberNodes[2] = m_scene->getRootSceneNode()->createChildSceneNode();


    if(GAME_TYPE == 1){
        m_gesture[0] = m_scene->createEntity("red.mesh");
        m_gestNode[0] = m_scene->getRootSceneNode()->createChildSceneNode();
        m_gestNode[0]->attachObject(m_gesture[0]);
        m_gestNode[0]->scale(6,1,10);

        m_gestNode[0]->setPosition(700,200,700);

    }

    this->setScore(0);
}

void World::createHealthPickup(int amount){
        addActor(new HealthPickup(this),Ogre::Vector3(rand() % m_width - (m_width/2), 0,  (rand() % (m_height/3)+m_height/3) + 800));
}

void World::createTestActor(int amount){
    for(int i = 0; i < amount; i++){
        addActor(new TestActor(this),Ogre::Vector3(rand() % m_width - (m_width/2), 0,  (rand() % (m_height/3)+m_height/3) + 800));
    }
}

void World::createSprinter(int amount){
    for(int i = 0; i < amount; i++){
        addActor(new Sprinter(this),Ogre::Vector3(rand() % m_width - (m_width/2), 0,  (rand() % (m_height/3)+m_height/3) + 800));
    }
}

void World::createFlyer(int amount){
    for(int i = 0; i < amount; i++){
           addActor(new Flyer(this),Ogre::Vector3(rand() % m_width - (m_width/2), 0,  (rand() % (m_height/3)+m_height/3) + 800));
    }
}

void World::createBossLeft(int amount){
    for(int i = 0; i < amount; i++){
        addActor(new BigBoss(this),Ogre::Vector3(1700, 0, 1200));
    }
}

void World::createBossRight(int amount){
    for(int i = 0; i < amount; i++){
        addActor(new BigBoss(this),Ogre::Vector3(-1700, 0, 1200));
    }
}

void World::addActor(AI* actor, Ogre::Vector3 position){
    actor->setPosition(position);
    m_actors.push_back(actor);
}

void World::removeActor(AI* actor){
    for(int i = 0; i < m_actors.size(); i++){
        if(m_actors.at(i) == actor){
            Ogre::Vector3 lastPos = m_actors.at(i)->getPosition();
            if(actor->getType() == TYPE_PLAYER || actor->getType() == TYPE_TEST_ACTOR || actor->getType() == TYPE_SPRINTER || actor->getType() == TYPE_FLYER){
                actor->animDeath(lastPos);
                if(actor->getType() != TYPE_PLAYER){
                }
            }
            removeActor(i);
            return;
        }
    }
}

void World::removeActor(int i){
    m_actors_toDie.push_back(i);
}

void World::killDiedActors(){
    if(m_actors_toDie.size() > 0){
        std::sort(m_actors_toDie.begin(), m_actors_toDie.end());
        m_actors_toDie.erase(std::unique(m_actors_toDie.begin(), m_actors_toDie.end()), m_actors_toDie.end());

        for(int i = m_actors_toDie.size()-1; i >= 0; i--){
            m_actors.at(m_actors_toDie.at(i))->destroy();
            m_actors.erase(m_actors.begin()+m_actors_toDie.at(i));
        }

        m_actors_toDie.erase(m_actors_toDie.begin(), m_actors_toDie.end());
    }
}

void World::deleteExternals(){
    for(int i = 0;i < m_actors.size(); i++){
        AI* obj = getActor(i);
        if(obj->getPosition().x > getHeight() ||
                obj->getPosition().x < 0-getHeight() ||
                obj->getPosition().z > getWidth() ||
                obj->getPosition().z < 0-getWidth()){
            m_actors_toDie.push_back(i);
        }
    }
}

void World::worldStep(float time){
    int i = 0;
    bool createWave = true;
    for(;i < m_actors.size(); i++){
        m_actors[i]->move(time);
        if(m_actors[i]->getType() == TYPE_TEST_ACTOR ||
                m_actors[i]->getType() == TYPE_SPRINTER){
            createWave = false;
        }
    }

    collisionDetection();
    deleteExternals();
    killDiedActors();
    spawner();

    if(Wii::isButtonDown(A) && !m_gestureRunning && m_gestCount != 0 && m_tracker->gridDrawn){
        m_tracker->deleteGrid();
    }

    if(Wii::isButtonDown(A) && !m_gestureRunning && m_gestCount != 0 && m_tracker->m_panelsDrawn){
        m_tracker->deletePanels();
    }

    if(Wii::isButtonDown(A) && !m_gestureRunning && m_gestCount != 0){
        // if button is down and the gesture is not running, delete all tracker markers and the square
        m_tracker->deleteMarkers();
        std::cout << "GESTURE RESTART!\n";
        // m_tracker->deleteSquares();
    }

    if(Wii::isButtonDown(A)){
        m_gestureRunning = true;
        m_gestCount++;
        m_tracker->setTracker(Wii::getVector(time));
    }

    if(!Wii::isButtonDown(A) && m_gestureRunning){
        std::cout << "GESTURE COMPLETED!\n";
        m_tracker->analyseGesture();
        m_tracker->identifyGesture();
        setGestureRunning(false);
    }
}

int World::getCurrentTime(){
    return timer.getMilliseconds();
}

void World::collisionDetection(){
    for(int i = 0;i < m_actors.size(); i++){
        if(getActor(i)->colides()){
            for(int loop = i+1;loop < m_actors.size(); loop++){
                if(getActor(loop)->colides()){
                    Ogre::Vector3 diff = getActor(i)->getPosition() - getActor(loop)->getPosition();

                    if(diff.length() < (COLLISION_RADIUS + 150) && m_player->m_deflecting &&
                       (getActor(i)->getType() == TYPE_PLAYER && getActor(loop)->getType() == TYPE_FLAMER &&
                        getActor(loop)->isNpc())){
                            getActor(loop)->colide(getActor(i));
                    }
                    if(diff.length() < (COLLISION_RADIUS + 100) && m_player->m_deflecting &&
                       (getActor(loop)->getType() == TYPE_PLAYER && getActor(i)->getType() == TYPE_FLAMER &&
                        getActor(i)->isNpc())){
                            getActor(loop)->colide(getActor(i));
                    }

                    else if(diff.length() < COLLISION_RADIUS){
                            getActor(i)->colide(getActor(loop));
                            getActor(loop)->colide(getActor(i));
                    }
                }
            }
        }
    }
}

void World::setScore(int score){

    m_numberNodes[0]->detachAllObjects();
    m_numberNodes[1]->detachAllObjects();
    m_numberNodes[2]->detachAllObjects();

    if(score <= 0){
        score = 0;
    }

    int ones = score % 10;
    int tens = score;
    if (score >= 100){
        tens = floor((score-100) / 10);
    }
    else{
        tens =  floor(score / 10);
    }
    int hundreds = floor(score / 100);

    std::cout << "SCOOOOORE UPDATE: " << hundreds << tens << ones << "\n";

    switch(ones){
    case 0:
        m_scene->destroyEntity("zero.mesh");
        m_numberNodes[0]->attachObject(m_scene->createEntity("zero.mesh"));
        break;
    case 1:
        m_numberNodes[0]->attachObject(m_scene->createEntity("one.mesh"));
        break;
    case 2:
        m_numberNodes[0]->attachObject(m_scene->createEntity("two.mesh"));
        break;
    case 3:
        m_numberNodes[0]->attachObject(m_scene->createEntity("three.mesh"));
        break;
    case 4:
        m_numberNodes[0]->attachObject(m_scene->createEntity("four.mesh"));
        break;
    case 5:
        m_numberNodes[0]->attachObject(m_scene->createEntity("five.mesh"));
        break;
    case 6:
        m_numberNodes[0]->attachObject(m_scene->createEntity("six.mesh"));
        break;
    case 7:
        m_numberNodes[0]->attachObject(m_scene->createEntity("seven.mesh"));
        break;
    case 8:
        m_numberNodes[0]->attachObject(m_scene->createEntity("eight.mesh"));
        break;
    case 9:
        m_numberNodes[0]->attachObject(m_scene->createEntity("nine.mesh"));
        break;
    }

    switch(tens){
    case 0:
        m_numberNodes[1]->attachObject(m_scene->createEntity("zero.mesh"));
        break;
    case 1:
        m_numberNodes[1]->attachObject(m_scene->createEntity("one.mesh"));
        break;
    case 2:
        m_numberNodes[1]->attachObject(m_scene->createEntity("two.mesh"));
        break;
    case 3:
        m_numberNodes[1]->attachObject(m_scene->createEntity("three.mesh"));
        break;
    case 4:
        m_numberNodes[1]->attachObject(m_scene->createEntity("four.mesh"));
        break;
    case 5:
        m_numberNodes[1]->attachObject(m_scene->createEntity("five.mesh"));
        break;
    case 6:
        m_numberNodes[1]->attachObject(m_scene->createEntity("six.mesh"));
        break;
    case 7:
        m_numberNodes[1]->attachObject(m_scene->createEntity("seven.mesh"));
        break;
    case 8:
        m_numberNodes[1]->attachObject(m_scene->createEntity("eight.mesh"));
        break;
    case 9:
        m_numberNodes[1]->attachObject(m_scene->createEntity("nine.mesh"));
        break;
    }
    switch(hundreds){
    case 0:
        m_numberNodes[2]->attachObject(m_scene->createEntity("zero.mesh"));
        break;
    case 1:
        m_numberNodes[2]->attachObject(m_scene->createEntity("one.mesh"));
        break;
    case 2:
        m_numberNodes[2]->attachObject(m_scene->createEntity("two.mesh"));
        break;
    case 3:
        m_numberNodes[2]->attachObject(m_scene->createEntity("three.mesh"));
        break;
    case 4:
        m_numberNodes[2]->attachObject(m_scene->createEntity("four.mesh"));
        break;
    case 5:
        m_numberNodes[2]->attachObject(m_scene->createEntity("five.mesh"));
        break;
    case 6:
        m_numberNodes[2]->attachObject(m_scene->createEntity("six.mesh"));
        break;
    case 7:
        m_numberNodes[2]->attachObject(m_scene->createEntity("seven.mesh"));
        break;
    case 8:
        m_numberNodes[2]->attachObject(m_scene->createEntity("eight.mesh"));
        break;
    case 9:
        m_numberNodes[2]->attachObject(m_scene->createEntity("nine.mesh"));
        break;
    }

    m_numberNodes[0]->setPosition(-1180,200,-110);
    m_numberNodes[0]->setScale(0.25,0,0.4);

    m_numberNodes[1]->setPosition(-1080,200,-110);
    m_numberNodes[1]->setScale(0.25,0,0.4);

    m_numberNodes[2]->setPosition(-980,200,-110);
    m_numberNodes[2]->setScale(0.25,0,0.4);

}

void World::spawner(){

    int spawning = getCurrentTime()/1000;

    if(GAME_TYPE == 2){

    switch(spawning){
    case 10:
        if(m_controller == 1){
            createFlyer(8);
            m_controller++;
        }
        break;
    case 12:
        if(m_controller == 2){
            createFlyer(8);
            m_controller++;
        }
        break;
    case 20:
        if(m_controller == 3){
             createFlyer(8);
             //createTestActor(2);
            m_controller++;
        }
        break;
    case 22:
        if(m_controller == 4){
             createFlyer(8);
             //createTestActor(2);
            m_controller++;
        }
        break;
    case 32:
        if(m_controller == 5){
            createTestActor(6);
            m_controller++;
        }
        break;
    case 42:
        if(m_controller == 6){
            createTestActor(6);
            m_controller++;
        }
        break;
    case 60:
        if(m_controller == 7){
            createBossLeft(1);
            m_controller++;
        }
        break;
    case 80:
        if(m_controller == 8){
            createBossRight(1);
            m_controller++;
        }
        break;
    case 100:
        if(m_controller == 9){
            createBossLeft(1);
            m_controller++;
        }
        break;
    }
    }
    else if (GAME_TYPE == 1){
        switch(spawning){
        case 6:
            if(m_controller == 1){
                GESTURE_SET == 1 ? m_gesture[0]->setMaterialName("sp1") : m_gesture[0]->setMaterialName("sp2");
                std::cout << "SETTING THE F****** MATERIAL!!!\n";
                m_controller++;
            }
            break;
        case 26:
            if(m_controller == 2){
                m_gesture[0]->setMaterialName("red");
                m_controller++;
            }
            break;
        case 27:
            if(m_controller == 3){
                GESTURE_SET == 1 ? m_gesture[0]->setMaterialName("sh1") : m_gesture[0]->setMaterialName("sh2");
                m_controller++;
            }
            break;
        case 47:
            if(m_controller == 4){
                m_gesture[0]->setMaterialName("red");
                m_controller++;
            }
            break;
        case 48:
            if(m_controller == 5){
                GESTURE_SET == 1 ? m_gesture[0]->setMaterialName("fl1") : m_gesture[0]->setMaterialName("fl2");
                m_controller++;
            }
            break;
        case 68:
            if(m_controller == 6){
                m_gesture[0]->setMaterialName("red");
                m_controller++;
            }
            break;
        }
    }
}

void World::setGestureRunning(bool gestureRunning){
    m_gestureRunning = gestureRunning;
}
